package com.ruoyi.workflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.constant.TaskConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.domain.PageQuery;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.page.TableDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.JsonUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.flowable.common.constant.ProcessConstants;
import com.ruoyi.flowable.common.enums.ProcessStatus;
import com.ruoyi.flowable.core.FormConf;
import com.ruoyi.flowable.core.domain.ProcessQuery;
import com.ruoyi.flowable.factory.FlowServiceFactory;
import com.ruoyi.flowable.flow.FlowableUtils;
import com.ruoyi.flowable.utils.ModelUtils;
import com.ruoyi.flowable.utils.ProcessFormUtils;
import com.ruoyi.flowable.utils.ProcessUtils;
import com.ruoyi.system.service.ISysDeptService;
import com.ruoyi.system.service.ISysRoleService;
import com.ruoyi.system.service.ISysUserService;
import com.ruoyi.workflow.domain.WfDeployForm;
import com.ruoyi.workflow.domain.vo.*;
import com.ruoyi.workflow.mapper.WfDeployFormMapper;
import com.ruoyi.workflow.service.IWfProcessService;
import com.ruoyi.workflow.service.IWfTaskService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricActivityInstanceQuery;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.identitylink.api.history.HistoricIdentityLink;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskQuery;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.flowable.task.api.history.HistoricTaskInstanceQuery;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author lgy
 * @createTime 2022/3/24 18:57
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WfProcessServiceImpl extends FlowServiceFactory implements IWfProcessService {
    private final IWfTaskService wfTaskService;
    private final ISysRoleService roleService;
    private final ISysDeptService deptService;
    @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection")
    private final WfDeployFormMapper deployFormMapper;
    private final ISysUserService userService;

    /**
     * 流程定义列表
     *
     * @param pageQuery 分页参数
     * @return 流程定义分页列表数据
     */
    @Override
    public TableDataInfo<WfDefinitionVo> selectPageStartProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
        // 流程定义列表数据查询
        ProcessDefinitionQuery pdQuery = repositoryService
                .createProcessDefinitionQuery()
                .latestVersion()
                //  必需是处于激活状态的流程定义（激活状态才能流程发起）
                .active()
                .orderByProcessDefinitionKey()
                .desc();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(pdQuery, processQuery);
        long pageTotal = pdQuery.count();
        if (pageTotal <= 0) {
            return TableDataInfo.build();
        } else {
            Page<WfDefinitionVo> page = new Page<>();
            page.setRecords(buildDefVoList(pageQuery, pdQuery));
            page.setTotal(pageTotal);
            return TableDataInfo.build(page);
        }
    }

    @Override
    public List<WfDefinitionVo> selectStartProcessList(ProcessQuery processQuery) {
        // 流程定义列表数据查询
        ProcessDefinitionQuery pdQuery = repositoryService
                .createProcessDefinitionQuery()
                .latestVersion()
                .active()
                .orderByProcessDefinitionKey()
                .asc();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(pdQuery, processQuery);
        List<ProcessDefinition> definitionList = pdQuery.list();
        return buildDefVoList(definitionList);
    }

    /**
     * 查询我的流程列表：谁开启的流程
     */
    @Override
    public TableDataInfo<WfTaskVo> selectPageOwnProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
        Page<WfTaskVo> page = new Page<>();
        HistoricProcessInstanceQuery hpiQuery = historyService
                .createHistoricProcessInstanceQuery()
                // 流程实例开启人
                .startedBy(SecurityUtils.getUserId().toString())
                // 开启时间倒序
                .orderByProcessInstanceStartTime()
                .desc();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(hpiQuery, processQuery);
        page.setTotal(hpiQuery.count());
        page.setRecords(buildTVFromHPIQ(pageQuery, hpiQuery));
        return TableDataInfo.build(page);
    }

    @Override
    public List<WfTaskVo> selectOwnProcessList(ProcessQuery processQuery) {
        HistoricProcessInstanceQuery hpiQuery = historyService
                .createHistoricProcessInstanceQuery()
                .startedBy(SecurityUtils.getUserId().toString())
                .orderByProcessInstanceStartTime()
                .desc();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(hpiQuery, processQuery);
        List<HistoricProcessInstance> historicProcessInstances = hpiQuery.list();
        List<WfTaskVo> taskVoList = new ArrayList<>();
        for (HistoricProcessInstance hisIns : historicProcessInstances) {
            WfTaskVo taskVo = new WfTaskVo();
            // 构造任务视图对象
            buildTVFromD(taskVo, hisIns);
            taskVoList.add(taskVo);
        }
        return taskVoList;
    }

    @Override
    public TableDataInfo<WfTaskVo> selectPageTodoProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
        Page<WfTaskVo> page = buildWfTaskVoPage(processQuery, pageQuery, false);
        return TableDataInfo.build(page);
    }

    /**
     * 任务查询
     */
    @Override
    public List<WfTaskVo> selectTodoProcessList(ProcessQuery processQuery) {
        return processList(processQuery, true);
    }

    /**
     * 任务查询
     */
    @Override
    public List<WfTaskVo> selectClaimProcessList(ProcessQuery processQuery) {
        return processList(processQuery, false);
    }


    @Override
    public TableDataInfo<WfTaskVo> selectPageClaimProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
        Page<WfTaskVo> page = buildWfTaskVoPage(processQuery, pageQuery, true);
        return TableDataInfo.build(page);
    }

    /**
     * 查询已办任务列表
     * */
    @Override
    public TableDataInfo<WfTaskVo> selectPageFinishedProcessList(ProcessQuery processQuery, PageQuery pageQuery) {
        Page<WfTaskVo> page = new Page<>();
        HistoricTaskInstanceQuery htiQuery = buildFinishedQuery();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(htiQuery, processQuery);
        // 总记录数
        page.setTotal(htiQuery.count());
        // 分页数据
        page.setRecords(buildFinTaskList(pageQuery, htiQuery));
        return TableDataInfo.build(page);
    }

    /**
     * 查询已办任务列表
     * */
    @Override
    public List<WfTaskVo> selectFinishedProcessList(ProcessQuery processQuery) {
        HistoricTaskInstanceQuery htiQuery = buildFinishedQuery();
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(htiQuery, processQuery);
        return buildVoList(htiQuery.list());
    }

    private HistoricTaskInstanceQuery buildFinishedQuery() {
        return historyService
                .createHistoricTaskInstanceQuery()
                // 获取流程变量数据
                .includeProcessVariables()
                // 已完成的
                .finished()
                // 指定办理人
                .taskAssignee(SecurityUtils.getUserId().toString())
                // 结束时间倒序
                .orderByHistoricTaskInstanceEndTime().desc();
    }

    /**
     * 构建已办任务列表
     * 分页
     * */
    private List<WfTaskVo> buildFinTaskList(PageQuery pageQuery, HistoricTaskInstanceQuery taskInstanceQuery) {
        int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        List<HistoricTaskInstance> historicTaskInstanceList = taskInstanceQuery.listPage(offset, pageQuery.getPageSize());
        return buildVoList(historicTaskInstanceList);
    }

    @Override
    public FormConf selectFormContent(String definitionId, String deployId, String procInsId) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(definitionId);
        if (ObjectUtil.isNull(bpmnModel)) {
            throw new RuntimeException("获取流程设计失败！");
        }
        StartEvent startEvent = ModelUtils.getStartEvent(bpmnModel);
        WfDeployForm deployForm = deployFormMapper.selectOne(new LambdaQueryWrapper<WfDeployForm>()
                .eq(WfDeployForm::getDeployId, deployId)
                .eq(WfDeployForm::getFormKey, startEvent.getFormKey())
                .eq(WfDeployForm::getNodeKey, startEvent.getId()));
        FormConf formConf = JsonUtils.parseObject(deployForm.getContent(), FormConf.class);
        if (ObjectUtil.isNull(formConf)) {
            throw new RuntimeException("获取流程表单失败！");
        }
        if (ObjectUtil.isNotEmpty(procInsId)) {
            // 获取流程实例
            HistoricProcessInstance historicProcIns = historyService.createHistoricProcessInstanceQuery()
                    .processInstanceId(procInsId)
                    .includeProcessVariables()
                    .singleResult();
            // 填充表单信息
            ProcessFormUtils.fillFormData(formConf, historicProcIns.getProcessVariables());
        }
        return formConf;
    }

    /**
     * 根据流程定义ID启动流程实例
     *
     * @param procDefId 流程定义Id
     * @param variables 流程变量
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void startProcessByDefId(String procDefId, Map<String, Object> variables) {
        try {
            ProcessDefinition processDefinition = repositoryService
                    .createProcessDefinitionQuery()
                    .processDefinitionId(procDefId)
                    .singleResult();
            startProcess(processDefinition, variables);
        } catch (Exception e) {
            log.info("流程启动错误:{}", e.getMessage());
            throw new ServiceException("流程启动错误");
        }
    }

    /**
     * 通过DefinitionKey启动流程
     *
     * @param procDefKey 流程定义Key
     * @param variables  扩展参数
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void startProcessByDefKey(String procDefKey, Map<String, Object> variables) {
        try {
            ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                    .processDefinitionKey(procDefKey).latestVersion().singleResult();
            startProcess(processDefinition, variables);
        } catch (Exception e) {
            log.info("流程启动错误 : {}", e.getMessage());
            throw new ServiceException("流程启动错误");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteProcessByIds(String[] instanceIds) {
        List<String> ids = Arrays.asList(instanceIds);
        // 校验流程是否结束
        long activeInsCount = runtimeService
                .createProcessInstanceQuery()
                .processInstanceIds(new HashSet<>(ids))
                .active()
                .count();
        if (activeInsCount > 0) {
            throw new ServiceException("不允许删除进行中的流程实例");
        }
        // 删除历史流程实例
        historyService.bulkDeleteHistoricProcessInstances(ids);
    }

    /**
     * 读取xml文件
     *
     * @param processDefId 流程定义ID
     */
    @Override
    public String queryBpmnXmlById(String processDefId) {
        InputStream inputStream = repositoryService.getProcessModel(processDefId);
        try {
            return IoUtil.readUtf8(inputStream);
        } catch (IORuntimeException exception) {
            throw new RuntimeException("加载xml文件异常");
        }
    }

    /**
     * 流程详情信息
     *
     * @param procInsId 流程实例ID
     * @param taskId    任务ID
     */
    @Override
    public WfDetailVo queryProcessDetail(String procInsId, String taskId) {
        WfDetailVo detailVo = new WfDetailVo();
        // 获取历史流程实例信息（可以从中获取全局变量）
        HistoricProcessInstance historicProcIns = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInsId)
                .includeProcessVariables()
                .singleResult();
        if (StringUtils.isNotBlank(taskId)) {
            // 获取历史任务实例
            HistoricTaskInstance taskIns = historyService
                    .createHistoricTaskInstanceQuery()
                    .taskId(taskId)
                    // 包含身份链接(任务执行人)
                    .includeIdentityLinks()
                    // 包含流程变量(流程节点表单内容)
                    .includeProcessVariables()
                    // 包含本地流程变量(同流程变量)
                    .includeTaskLocalVariables()
                    .singleResult();
            if (taskIns == null) {
                throw new ServiceException("没有可办理的任务！");
            }
            // 获取流程表单信息
            detailVo.setTaskFormData(buildFormData(historicProcIns.getDeploymentId(), taskIns));
        }
        // 获取Bpmn模型信息
        InputStream inputStream = repositoryService.getProcessModel(historicProcIns.getProcessDefinitionId());
        // IoUtil.readBytes():从流中读取bytes
        String bpmnXmlStr = StrUtil.utf8Str(IoUtil.readBytes(inputStream, false));
        BpmnModel bpmnModel = ModelUtils.getBpmnModel(bpmnXmlStr);
        // 可以直接获取bpmnModel: bpmnModel = repositoryService.getBpmnModel(historicProcIns.getProcessDefinitionId());
        detailVo.setBpmnXml(bpmnXmlStr);
        // 历史任务信息列表
        detailVo.setHistoryProcNodeList(historyProcNodeList(historicProcIns));
        // 获取流程表单信息
        detailVo.setProcessFormList(processFormList(bpmnModel, historicProcIns));
        // 获取流程执行过程
        detailVo.setFlowViewer(getFlowViewer(bpmnModel, procInsId));
        return detailVo;
    }

    @Override
    public StringBuilder buildStringBuilder(String taskId) {
        // 查询相关的历史身份信息链接，这些信息包括参与流程的用户、组等身份标识，
        // 可用于追溯和审计流程执行过程中涉及的人员身份情况，方便查看是谁在什么时间以何种身份参与了流程的各个环节。
        List<HistoricIdentityLink> linksForTask = historyService.getHistoricIdentityLinksForTask(taskId);
        StringBuilder stringBuilder = new StringBuilder();
        for (HistoricIdentityLink identityLink : linksForTask) {
            // 候选人
            if ("candidate".equals(identityLink.getType())) {
                // 候选人：具体到某个人
                if (StringUtils.isNotBlank(identityLink.getUserId())) {
                    Long userId = Long.parseLong(identityLink.getUserId());
                    String nickName = userService.selectNickNameById(userId);
                    stringBuilder.append(nickName).append(",");
                }
                // 候选人：候选人组（角色、部门）
                if (StringUtils.isNotBlank(identityLink.getGroupId())) {
                    if (identityLink.getGroupId().startsWith(TaskConstants.ROLE_GROUP_PREFIX)) {
                        // 角色
                        Long roleId = Long.parseLong(StringUtils.stripStart(identityLink.getGroupId(), TaskConstants.ROLE_GROUP_PREFIX));
                        SysRole role = roleService.selectRoleById(roleId);
                        stringBuilder.append(role.getRoleName()).append(",");
                    } else if (identityLink.getGroupId().startsWith(TaskConstants.DEPT_GROUP_PREFIX)) {
                        // 部门
                        Long deptId = Long.parseLong(StringUtils.stripStart(identityLink.getGroupId(), TaskConstants.DEPT_GROUP_PREFIX));
                        SysDept dept = deptService.selectDeptById(deptId);
                        stringBuilder.append(dept.getDeptName()).append(",");
                    }
                }
            }
        }
        return stringBuilder;
    }

    private List<WfDefinitionVo> buildDefVoList(PageQuery pageQuery, ProcessDefinitionQuery pdQuery) {
        int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        List<ProcessDefinition> definitionList = pdQuery.listPage(offset, pageQuery.getPageSize());
        return buildDefVoList(definitionList);
    }


    /**
     * 构建流程定义视图对象集合
     */
    private List<WfDefinitionVo> buildDefVoList(List<ProcessDefinition> definitionList) {
        List<WfDefinitionVo> definitionVoList = new ArrayList<>();
        for (ProcessDefinition processDefinition : definitionList) {
            String deploymentId = processDefinition.getDeploymentId();
            Deployment deployment = repositoryService
                    .createDeploymentQuery()
                    .deploymentId(deploymentId)
                    .singleResult();
            definitionVoList.add(buildVo(processDefinition, deployment));
        }
        return definitionVoList;
    }

    /**
     * 构建流程定义视图对象
     */
    private WfDefinitionVo buildVo(ProcessDefinition processDefinition, Deployment deployment) {
        WfDefinitionVo vo = new WfDefinitionVo();
        vo.setDefinitionId(processDefinition.getId());
        vo.setProcessKey(processDefinition.getKey());
        vo.setProcessName(processDefinition.getName());
        vo.setVersion(processDefinition.getVersion());
        vo.setDeploymentId(processDefinition.getDeploymentId());
        vo.setSuspended(processDefinition.isSuspended());
        // 流程定义时间
        vo.setCategory(deployment.getCategory());
        vo.setDeploymentTime(deployment.getDeploymentTime());
        return vo;
    }


    /**
     * 构造历史流程实例视图对象集合
     */
    private List<WfTaskVo> buildTVFromHPIQ(PageQuery pageQuery, HistoricProcessInstanceQuery hpiQuery) {
        int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        List<HistoricProcessInstance> hpiList = hpiQuery.listPage(offset, pageQuery.getPageSize());
        List<WfTaskVo> taskVoList = new ArrayList<>();
        for (HistoricProcessInstance hpi : hpiList) {
            WfTaskVo taskVo = new WfTaskVo();
            // 获取流程状态(运行中、已完成、已终止、已取消)
            HistoricVariableInstance hvi = historyService
                    .createHistoricVariableInstanceQuery()
                    .processInstanceId(hpi.getId())
                    // 流程状态变量名称
                    .variableName(ProcessConstants.PROCESS_STATUS_KEY)
                    .singleResult();
            String processStatus = null;
            if (ObjectUtil.isNotNull(hvi)) {
                processStatus = Convert.toStr(hvi.getValue());
            }
            // 兼容旧流程
            if (processStatus == null) {
                processStatus = ObjectUtil.isNull(hpi.getEndTime()) ? ProcessStatus.RUNNING.getStatus() : ProcessStatus.COMPLETED.getStatus();
            }
            taskVo.setProcessStatus(processStatus);
            //构造任务视图对象
            buildTVFromD(taskVo, hpi);
            taskVoList.add(taskVo);
        }
        return taskVoList;
    }

    /**
     * 构造任务视图对象
     */
    private void buildTVFromD(WfTaskVo taskVo, HistoricProcessInstance hisIns) {
        taskVo.setCreateTime(hisIns.getStartTime());
        taskVo.setFinishTime(hisIns.getEndTime());
        taskVo.setProcInsId(hisIns.getId());
        // 计算耗时
        if (Objects.nonNull(hisIns.getEndTime())) {
            taskVo.setDuration(DateUtils.getDatePoor(hisIns.getEndTime(), hisIns.getStartTime()));
        } else {
            taskVo.setDuration(DateUtils.getDatePoor(DateUtils.getNowDate(), hisIns.getStartTime()));
        }
        // 流程部署实例信息
        Deployment deployment = repositoryService
                .createDeploymentQuery()
                .deploymentId(hisIns.getDeploymentId())
                .singleResult();
        taskVo.setDeployId(hisIns.getDeploymentId());
        taskVo.setProcDefId(hisIns.getProcessDefinitionId());
        taskVo.setProcDefName(hisIns.getProcessDefinitionName());
        taskVo.setProcDefVersion(hisIns.getProcessDefinitionVersion());
        taskVo.setCategory(deployment.getCategory());
        // 当前所处流程实例任务
        List<Task> taskList = taskService
                .createTaskQuery()
                .processInstanceId(hisIns.getId())
                .includeIdentityLinks()
                .list();
        if (CollUtil.isNotEmpty(taskList)) {
            taskVo.setTaskName(taskList.stream().map(Task::getName).filter(StringUtils::isNotEmpty).collect(Collectors.joining(",")));
        }
    }

    /**
     * 构建工作流任务视图对象
     * */
    private WfTaskVo buildTv(HistoricTaskInstance hti) {
        WfTaskVo flowTask = new WfTaskVo();
        // 当前流程信息
        flowTask.setTaskId(hti.getId());
        // 审批人员信息
        flowTask.setCreateTime(hti.getCreateTime());
        flowTask.setFinishTime(hti.getEndTime());
        flowTask.setDuration(DateUtil.formatBetween(hti.getDurationInMillis(), BetweenFormatter.Level.SECOND));
        flowTask.setProcDefId(hti.getProcessDefinitionId());
        flowTask.setTaskDefKey(hti.getTaskDefinitionKey());
        flowTask.setTaskName(hti.getName());
        flowTask.setProcInsId(hti.getProcessInstanceId());
        flowTask.setHisProcInsId(hti.getProcessInstanceId());
        // 流程变量
        flowTask.setProcVars(hti.getProcessVariables());
        return flowTask;
    }

    private WfTaskVo buildTv(Task task) {
        WfTaskVo flowTask = new WfTaskVo();
        // 当前流程信息
        flowTask.setTaskId(task.getId());
        flowTask.setTaskDefKey(task.getTaskDefinitionKey());
        flowTask.setCreateTime(task.getCreateTime());
        flowTask.setProcDefId(task.getProcessDefinitionId());
        flowTask.setTaskName(task.getName());
        flowTask.setProcInsId(task.getProcessInstanceId());
        // 其它信息
        buildTv(flowTask, task.getProcessDefinitionId(), task.getProcessInstanceId());
        return flowTask;
    }

    private void buildTv(WfTaskVo flowTask, String pdId, String pId) {
        // 流程定义信息
        ProcessDefinition pd = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionId(pdId)
                .singleResult();
        flowTask.setDeployId(pd.getDeploymentId());
        flowTask.setProcDefName(pd.getName());
        flowTask.setProcDefVersion(pd.getVersion());
        // 流程发起人信息
        HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(pId)
                .singleResult();
        Long userId = Long.parseLong(historicProcessInstance.getStartUserId());
        String nickName = userService.selectNickNameById(userId);
        flowTask.setStartUserId(userId);
        flowTask.setStartUserName(nickName);
    }

    /**
     * 查询任务列表：flag=true:待办任务，flag=false:待签收任务
     */
    private List<WfTaskVo> processList(ProcessQuery processQuery, boolean flag) {
        TaskQuery taskQuery = taskService
                .createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateGroupIn(SecurityUtils.getCandidateGroup())
                .orderByTaskCreateTime()
                .desc();
        if (flag) {
            taskQuery.taskCandidateOrAssigned(SecurityUtils.getUserId().toString());
        } else {
            taskQuery.taskCandidateUser(SecurityUtils.getUserId().toString());
        }
        return buildTVFromTQ(taskQuery, processQuery);
    }

    private List<WfTaskVo> buildTVFromTQ(TaskQuery taskQuery, ProcessQuery processQuery) {
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(taskQuery, processQuery);
        List<Task> taskList = taskQuery.list();
        List<WfTaskVo> taskVoList = new ArrayList<>();
        for (Task task : taskList) {
            WfTaskVo taskVo = buildTv(task);
            taskVoList.add(taskVo);
        }
        return taskVoList;
    }

    /**
     * 获取任务列表：
     * flag：true：待签收，
     * false：待处理
     */
    private Page<WfTaskVo> buildWfTaskVoPage(ProcessQuery processQuery, PageQuery pageQuery, boolean flag) {
        Page<WfTaskVo> page = new Page<>();
        TaskQuery taskQuery = taskService
                .createTaskQuery()
                .active()
                .includeProcessVariables()
                .taskCandidateGroupIn(SecurityUtils.getCandidateGroup())
                .orderByTaskCreateTime()
                .desc();
        if (flag) {
            // 设置查询条件，专门查询那些指定用户作为候选人的任务。
            taskQuery.taskCandidateUser(SecurityUtils.getLoginUser().toString());
        } else {
            // 设置查询条件：查询那些当前用户既可以作为候选人（candidate）参与，又可以作为实际分配人（assigned）负责的任务。
            taskQuery.taskCandidateOrAssigned(SecurityUtils.getUserId().toString());
        }
        // 构建搜索条件
        ProcessUtils.buildProcessSearch(taskQuery, processQuery);
        page.setTotal(taskQuery.count());
        int offset = pageQuery.getPageSize() * (pageQuery.getPageNum() - 1);
        List<Task> taskList = taskQuery.listPage(offset, pageQuery.getPageSize());
        List<WfTaskVo> flowList = new ArrayList<>();
        for (Task task : taskList) {
            WfTaskVo flowTask = buildTv(task);
            if (!flag) {
                // 流程变量
                flowTask.setProcVars(task.getProcessVariables());
            }
            flowList.add(flowTask);
        }
        page.setRecords(flowList);
        return page;
    }

    /**
     * 构建历史任务列表
     * */
    private List<WfTaskVo> buildVoList(List<HistoricTaskInstance> htiList) {
        List<WfTaskVo> hisTaskList = new ArrayList<>();
        for (HistoricTaskInstance hti : htiList) {
            WfTaskVo flowTask = buildTv(hti);
            // 其它信息
            buildTv(flowTask, hti.getProcessDefinitionId(), hti.getProcessInstanceId());
            hisTaskList.add(flowTask);
        }
        return hisTaskList;
    }

    /**
     * 启动流程实例
     */
    private void startProcess(ProcessDefinition procDef, Map<String, Object> variables) {
        if (ObjectUtil.isNotNull(procDef) && procDef.isSuspended()) {
            throw new ServiceException("流程已被挂起，请先激活流程");
        }
        // 设置流程发起人Id到流程中
        String userIdStr = String.valueOf(SecurityUtils.getUserId());
        identityService.setAuthenticatedUserId(userIdStr);
        // 流程发起人，开始事件（Start Event）是流程的起点，而“发起者”属性可能用于指定谁或者什么实体来触发这个开始事件。
        variables.put(BpmnXMLConstants.ATTRIBUTE_EVENT_START_INITIATOR, userIdStr);
        // 设置流程状态为进行中
        variables.put(ProcessConstants.PROCESS_STATUS_KEY, ProcessStatus.RUNNING.getStatus());
        // 依据流程定义id（流程定义id唯一），发起流程实例，同时设置流程变量（填充流程节点表单）
        ProcessInstance processInstance = runtimeService.startProcessInstanceById(procDef.getId(), variables);
        // 第一个用户任务为发起人，则自动完成任务
        wfTaskService.startFirstTask(processInstance, variables);
    }

    /**
     * 获取当前任务流程表单信息
     */
    private FormConf buildFormData(String deployId, HistoricTaskInstance taskIns) {
        // 表单信息存在与wf_deploy_form表中
        WfDeployFormVo deployFormVo = deployFormMapper
                .selectVoOne(new LambdaQueryWrapper<WfDeployForm>()
                        .eq(WfDeployForm::getDeployId, deployId)
                        .eq(WfDeployForm::getFormKey, taskIns.getFormKey())
                        .eq(WfDeployForm::getNodeKey, taskIns.getTaskDefinitionKey()));
        if (ObjectUtil.isNotEmpty(deployFormVo)) {
            // 获取表单信息:以json字符串的形式存在WfDeployFormVo的content字段中,需要转换为form对象
            FormConf form = JsonUtils.parseObject(deployFormVo.getContent(), FormConf.class);
            if (null != form) {
                // 隐藏表单按钮
                form.setFormBtns(false);
                // 填充表单项内容
                ProcessFormUtils.fillFormData(form, taskIns.getTaskLocalVariables());
                return form;
            }
        }
        return null;
    }

    /**
     * 获取历史流程表单信息
     */
    private List<FormConf> processFormList(BpmnModel bpmnModel, HistoricProcessInstance historicProcIns) {
        List<FormConf> procFormList = new ArrayList<>();
        List<HistoricActivityInstance> aiList = historyService
                .createHistoricActivityInstanceQuery()
                // 查询已完成的活动实例
                .processInstanceId(historicProcIns.getId()).finished()
                // 查询用户任务和开始事件
                .activityTypes(CollUtil.newHashSet(BpmnXMLConstants.ELEMENT_EVENT_START, BpmnXMLConstants.ELEMENT_TASK_USER))
                // 按开始时间升序
                .orderByHistoricActivityInstanceStartTime().asc()
                .list();
        List<String> processFormKeys = new ArrayList<>();
        for (HistoricActivityInstance ai : aiList) {
            // 获取当前节点流程元素信息
            FlowElement flowElement = ModelUtils.getFlowElementById(bpmnModel, ai.getActivityId());
            // 获取当前节点表单Key
            String formKey = ModelUtils.getFormKey(flowElement);
            if (formKey == null) {
                continue;
            }
            boolean localScope = Convert.toBool(ModelUtils.getElementAttributeValue(flowElement, ProcessConstants.PROCESS_FORM_LOCAL_SCOPE), false);
            Map<String, Object> variables;
            if (localScope) {
                // 查询任务节点参数，并转换成Map
                variables = historyService
                        .createHistoricVariableInstanceQuery()
                        .processInstanceId(historicProcIns.getId())
                        .taskId(ai.getTaskId())
                        .list()
                        .stream()
                        .collect(Collectors.toMap(HistoricVariableInstance::getVariableName, HistoricVariableInstance::getValue));
            } else {
                if (processFormKeys.contains(formKey)) {
                    continue;
                }
                variables = historicProcIns.getProcessVariables();
                processFormKeys.add(formKey);
            }
            // 非节点表单此处查询结果可能有多条，只获取第一条信息
            List<WfDeployFormVo> formInfoList = deployFormMapper
                    .selectVoList(new LambdaQueryWrapper<WfDeployForm>()
                    .eq(WfDeployForm::getDeployId, historicProcIns.getDeploymentId())
                    .eq(WfDeployForm::getFormKey, formKey)
                    .eq(localScope, WfDeployForm::getNodeKey, flowElement.getId()));

            //：避免空集合导致的NULL空指针
            WfDeployFormVo formInfo = formInfoList.stream().findFirst().orElse(null);
            if (ObjectUtil.isNotNull(formInfo)) {
                // 旧数据 formInfo.getFormName() 为 null
                String formName = Optional.ofNullable(formInfo.getFormName()).orElse(StringUtils.EMPTY);
                String title = localScope ? formName.concat("(" + flowElement.getName() + ")") : formName;
                FormConf formConf = JsonUtils.parseObject(formInfo.getContent(), FormConf.class);
                if (null != formConf) {
                    formConf.setTitle(title);
                    formConf.setDisabled(true);
                    formConf.setFormBtns(false);
                    ProcessFormUtils.fillFormData(formConf, variables);
                    procFormList.add(formConf);
                }
            }
        }
        return procFormList;
    }

    /**
     * 获取历史任务信息列表
     */
    private List<WfProcNodeVo> historyProcNodeList(HistoricProcessInstance historicProcIns) {
        String procInsId = historicProcIns.getId();
        List<HistoricActivityInstance> historicActivityInstanceList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId)
                // 类型为开始节点、结束节点、用户任务
                .activityTypes(CollUtil.newHashSet(BpmnXMLConstants.ELEMENT_EVENT_START, BpmnXMLConstants.ELEMENT_EVENT_END, BpmnXMLConstants.ELEMENT_TASK_USER))
                .orderByHistoricActivityInstanceStartTime().desc()
                .orderByHistoricActivityInstanceEndTime().desc()
                .list();
        // 获取流程实例批注信息
        List<Comment> commentList = taskService.getProcessInstanceComments(procInsId);
        List<WfProcNodeVo> elementVoList = new ArrayList<>();
        for (HistoricActivityInstance activityInstance : historicActivityInstanceList) {
            // 工作流节点元素视图对象
            elementVoList.add(buildProcNodeVo(activityInstance, historicProcIns, commentList));
        }
        return elementVoList;
    }

    /**
     * 构建：工作流节点元素视图对象
     * @param hai 历史活动实例
     * @param historicProcIns 历史流程实例
     * @param commentList 批注列表
     * @return 工作流节点元素视图对象
     */
    private WfProcNodeVo buildProcNodeVo(HistoricActivityInstance hai, HistoricProcessInstance historicProcIns, List<Comment> commentList) {
        WfProcNodeVo elementVo = new WfProcNodeVo();
        elementVo.setProcDefId(hai.getProcessDefinitionId());
        elementVo.setActivityId(hai.getActivityId());
        elementVo.setActivityName(hai.getActivityName());
        elementVo.setActivityType(hai.getActivityType());
        elementVo.setCreateTime(hai.getStartTime());
        elementVo.setEndTime(hai.getEndTime());
        if (ObjectUtil.isNotNull(hai.getDurationInMillis())) {
            // 持续时间：秒
            elementVo.setDuration(DateUtil.formatBetween(hai.getDurationInMillis(), BetweenFormatter.Level.SECOND));
        }
        if (BpmnXMLConstants.ELEMENT_EVENT_START.equals(hai.getActivityType())) {
            // 开始节点
            if (ObjectUtil.isNotNull(historicProcIns)) {
                Long userId = Long.parseLong(historicProcIns.getStartUserId());
                String nickName = userService.selectNickNameById(userId);
                if (nickName != null) {
                    elementVo.setAssigneeId(userId);
                    elementVo.setAssigneeName(nickName);
                }
            }
        } else if (BpmnXMLConstants.ELEMENT_TASK_USER.equals(hai.getActivityType())) {
            // 用户任务节点
            if (StringUtils.isNotBlank(hai.getAssignee())) {
                Long userId = Long.parseLong(hai.getAssignee());
                String nickName = userService.selectNickNameById(userId);
                elementVo.setAssigneeId(userId);
                elementVo.setAssigneeName(nickName);
            }
            StringBuilder stringBuilder = buildStringBuilder(hai.getTaskId());
            if (StringUtils.isNotBlank(stringBuilder)) {
                elementVo.setCandidate(stringBuilder.substring(0, stringBuilder.length() - 1));
            }
            // 获取意见评论内容
            if (CollUtil.isNotEmpty(commentList)) {
                List<Comment> comments = new ArrayList<>();
                for (Comment comment : commentList) {

                    if (comment.getTaskId().equals(hai.getTaskId())) {
                        comments.add(comment);
                    }
                }
                elementVo.setCommentList(comments);
            }
        }
        return elementVo;
    }

    /**
     * 获取流程执行过程
     */
    private WfViewerVo getFlowViewer(BpmnModel bpmnModel, String procInsId) {
        // 构建查询条件
        HistoricActivityInstanceQuery query = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(procInsId);
        List<HistoricActivityInstance> haiList = query.list();
        if (CollUtil.isEmpty(haiList)) {
            return new WfViewerVo();
        }
        // 查询所有已完成的元素
        List<HistoricActivityInstance> finishedElementList = haiList
                .stream()
                // 过滤条件：已完成（结束时间不为空）
                .filter(item -> ObjectUtil.isNotNull(item.getEndTime()))
                .collect(Collectors.toList());
        // 所有已完成的连线
        Set<String> finishedSequenceFlowSet = new HashSet<>();
        // 所有已完成的任务节点
        Set<String> finishedTaskSet = new HashSet<>();
        finishedElementList.forEach(item -> {
            // 顺序流元素（BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW），即：连线
            if (BpmnXMLConstants.ELEMENT_SEQUENCE_FLOW.equals(item.getActivityType())) {
                finishedSequenceFlowSet.add(item.getActivityId());
            } else {
                finishedTaskSet.add(item.getActivityId());
            }
        });
        // 查询所有未结束的节点
        Set<String> unfinishedTaskSet = haiList
                .stream()
                // 过滤条件：未结束（结束时间为空）
                .filter(item -> ObjectUtil.isNull(item.getEndTime()))
                .map(HistoricActivityInstance::getActivityId)
                .collect(Collectors.toSet());
        // DFS 查询未通过的元素集合
        Set<String> rejectedSet = FlowableUtils.dfsFindRejects(bpmnModel, unfinishedTaskSet, finishedSequenceFlowSet, finishedTaskSet);
        return new WfViewerVo(finishedTaskSet, finishedSequenceFlowSet, unfinishedTaskSet, rejectedSet);
    }
}
